import cv2
import numpy as np
import math


# 找出外接四边形, c是轮廓的坐标数组
def boundingBox(idx, c):
    if len(c) < 2:
        return None
    epsilon = 30
    while True:
        approxBox = cv2.approxPolyDP(c, epsilon, True)
        # 求出拟合得到的多边形的面积,连通区域，用格林公式
        theArea = math.fabs(cv2.contourArea(approxBox))
        # 输出拟合信息
        #print("contour idx: %d ,contour_len: %d ,epsilon: %d ,approx_len: %d ,approx_area: %s" % (idx, len(c), epsilon, len(approxBox), theArea))
        if (len(approxBox) < 4):
            return None
        if theArea > 0:
            if (len(approxBox) > 4):
                # epsilon 增长一个步长值
                epsilon += 1
                continue
            else:  # approx的长度为4，表明已经拟合成矩形了
                # 转换成4*2的数组
                approxBox = approxBox.reshape((4, 2))
                return approxBox
        else:
            #print("failed to find boundingBox,idx = %d area=%f" % (idx, theArea))
            return None

#对坐标点进行排序
def order_points(pts):
    # 初始化将要排序的坐标列表
    # 所以列表中的第一个条目是左上角，
    # 第二条是右上角，第三条是右下角，第四条是左下角
    rect = np.zeros((4, 2), dtype="int")

    # 左上角的和最小，而右下角的和最大
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    # 计算两个点之间的差异，右上角的差异最小，而左下角的差异最大，点的x,y做差，y-x，右上角为负数，左下角为正数，其余接近0
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]

    # [top-left, top-right, bottom-right, bottom-left]
    return rect

def main(path):
    # 开始图像处理，读取图片文件
    image = cv2.imread(path)
    srcHeight,srcWidth ,channels = image.shape
    #print(srcHeight,srcWidth)
    image_copy = image.copy()
    cv2.namedWindow('image',0)
    cv2.resizeWindow('image',srcWidth,srcHeight)
    cv2.imshow("image", image)
    #获取原始\目标图像的大小


    src_drc=np.array([[0, 0],[srcWidth, 0],[srcWidth, srcHeight],[0, srcHeight]])
    src_rect = np.float32([src_drc[0], src_drc[1], src_drc[2], src_drc[3]])

    #转成灰度图

    #image = cv2.GaussianBlur(image, (9,9),1,1)

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # 中值滤波平滑，消除噪声
    # 中值滤波
    binary = cv2.medianBlur(gray,5)
    #binary = cv2.GaussianBlur(binary, (9,9),1,1)
    #转换为二值图像
    ret, binary = cv2.threshold(binary, 160, 255, cv2.THRESH_BINARY)
    cv2.namedWindow('binary',0)
    cv2.resizeWindow('binary',srcWidth,srcHeight)
    cv2.imshow("binary", binary)
    #cv2.waitKey(0)

    # canny 边缘检测
    canny = cv2.Canny(binary, 190, 255, 1)
    cv2.namedWindow('canny', 0)
    cv2.resizeWindow('canny', srcWidth, srcHeight)
    cv2.imshow("canny", canny)

    kernel = np.ones((3, 3), np.uint8)
    pengzhang = cv2.dilate(canny, kernel, iterations=1)
    #显示边缘检测的结果
    cv2.namedWindow('pengzhang',0)
    cv2.resizeWindow('pengzhang',srcWidth,srcHeight)
    cv2.imshow("pengzhang", pengzhang)
    # 提取轮廓
    contours,_ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    '''
    ctous = cv2.drawContours(image, contours, -1, (0, 255, 0), 5)  # img为三通道才能显示轮廓
    cv2.namedWindow('ctous', 0)
    cv2.resizeWindow('ctous', srcWidth, srcHeight)
    cv2.imshow("ctous", ctous)
    '''
    ## 输出轮廓数目
    #print("the count of contours is  %d \n"%(len(contours)))

    # 针对每个轮廓，拟合外接四边形,如果成功，则将记录该四个点

    s=0#记录最大的轮廓面积
    BOX=np.zeros((4, 2), dtype="int")
    for idx, c in enumerate(contours):

        approxBox = boundingBox(idx, c)
        if approxBox is None:
            #        print("\n")
            continue

        # 获取最小矩形包络
        rect = cv2.minAreaRect(approxBox)# 得到最小外接矩形的（中心(x,y), (宽,高), 旋转角度），approxBox是array对象，任意点集
        box = cv2.boxPoints(rect)# 获取最小外接矩形的4个顶点坐标
        box = box.reshape(4, 2)
        box = order_points(box)
        if cv2.contourArea(box)>s:
            s=cv2.contourArea(box)
            BOX=box
    #画最大矩形轮廓
    print(s)
    color=(36, 255, 12)
    thickness=2
    cv2.line(image, tuple(BOX[0]), tuple(BOX[1]), color, thickness)
    cv2.line(image, tuple(BOX[1]), tuple(BOX[2]), color, thickness)
    cv2.line(image, tuple(BOX[2]), tuple(BOX[3]), color, thickness)
    cv2.line(image, tuple(BOX[3]), tuple(BOX[0]), color, thickness)
    #print("boundingBox：\n", box)
    #cv2.imwrite('pic/detected_yanmobianhuan.png', image)
    cv2.namedWindow('detected',0)
    cv2.resizeWindow('detected',srcWidth,srcHeight)
    cv2.imshow("detected", image)
    cv2.waitKey(0)

def start():
    main(r"pic/5.png")

start()